import numpy as np
import pandas as pd

# Series

data = pd.Series([0.25, 0.5, 0.75, 1.0])
# Series将一维数组与索引绑定在一起
data.values
data.index

# 1.Serise是通用的NumPy数组
# 索引不再仅仅是整数,还可以是任意想要的类型。
data = pd.Series([0.25, 0.5, 0.75, 1.0],
                 index=['a', 'b', 'c', 'd'])
# 可以使用不连续或不按顺序的索引
data = pd.Series([0.25, 0.5, 0.75, 1.0],
                 index=[2, 5, 3, 7])
data[5]
# 2.Series是特殊的字典
population_dict = {'California': 38332521,
                   'Texas': 26448193,
                   'New York': 19651127,
                   'Florida': 19552860,
                   'Illinois': 12882135}
population = pd.Series(population_dict)
population['California'] #在[n,1]数组中取某一行，类似key:value
population['California':'Illinois'] #支持切片，左闭右闭，应同于显式index

# 建立Series
pd.Series([2, 4, 6])
pd.Series(5, index=[100, 200, 300]) #单一值被复制
pd.Series({2: 'a', 1: 'b', 3: 'c'}) #以dict的keys大小对index排序，在Python 3.7.3
#中与书中所述不符。
data=pd.Series({2:'a', 1:'b', 3:'c'}, index=[3, 2]) #显式指定，此data中不包括index=[1]

# DataFrame

# 1.DataFrame作为通用NumPy数组
area_dict = {'California': 423967,
             'Texas': 695662,
             'New York': 141297,
             'Florida': 170312,
             'Illinois': 149995}
area = pd.Series(area_dict)

states=pd.DataFrame({'population': population, 'area': area})
states.index
states.columns

# 2.DataFrame作为特殊的字典
states['area'] #在[m,n]数组中，取某一列；结果不同于[n,1]中取某一行
# 二维NumPy数组，data[0]返回第一行；DataFrame中，data['col0']返回第一列。

# 建立DataFrame的多种方式：
# 1.从单个Series对象；a single Series object
pd.DataFrame(population, columns=['population'])
# 2.从dicts列表；a list of dicts。缺失值以NaN填充
data=[{'a': i, 'b': 2 * i}
      for i in range(3)]
pd.DataFrame(data)
# 3.从Series对象字典；a dictionary of Series objects
pd.DataFrame({'population': population, 'area': area})
# 4.从二维NumPy数组；a two-dimensional NumPy array
d4=pd.DataFrame(np.random.rand(3, 2),
                columns=['foo', 'bar'],
                index=['a', 'b', 'c'])
d4
# 5.从NumPy结构化数组；a NumPy structured array
A=np.zeros(3, dtype=[('A', 'i8'), ('B', 'f8')])
pd.DataFrame(A)

# Index

# 可看作 不可变数组(immutable array) 或 有序集合(ordered set)
ind = pd.Index([2, 3, 5, 7, 11])
ind[1]
ind[::2]
print(ind.size, ind.shape, ind.ndim, ind.dtype)

indA = pd.Index([1, 3, 5, 7, 9])
indB = pd.Index([2, 3, 5, 7, 11])
indA & indB
indA | indB
indA ^ indB
indA.intersection(indB)

# Series中取值
import pandas as pd
data=pd.Series([0.25, 0.5, 0.75, 1.0],
               index=['a', 'b', 'c', 'd'])
'a' in data
data.keys()
list(data.items())
data['e']=1.25 #增加新的index以扩展

data['a':'c'] #显式索引，左闭右闭
data[0:2] #隐式索引，左闭右开，同切片
data[(data > 0.3) & (data < 0.8)] #掩码
data[['a', 'e']] #花哨的索引

data=pd.Series(['a', 'b', 'c'], index=[1, 3, 5])
data[1] #取值是显式
data[1:5] #切片是隐式

data.loc[1]
data.loc[1:3]
# loc取值和切片都是显式

data.iloc[1]
data.iloc[1:3]
# iloc取值和切片都是隐式

# DataFrame中取值
area = pd.Series({'California': 423967, 'Texas': 695662,
                  'New York': 141297, 'Florida': 170312,
                  'Illinois': 149995})
pop = pd.Series({'California': 38332521, 'Texas': 26448193,
                 'New York': 19651127, 'Florida': 19552860,
                 'Illinois': 12882135})
data = pd.DataFrame({'area':area, 'pop':pop})
data['area']
data.area #类似R之'$'
data.area is data['area']
data.pop is data['pop'] #存在同名pop()函数

data['density']=data['pop'] / data['area'] #以字典形式增加一列

data.T #transpose
data.values
data.values[0] #取一行
data['area'] #取一列，同data[:, 0]
data.iloc[:3, :2] #切片为隐式
data.loc[:'Illinois', :'pop'] #索引为显式
data.ix[:3, :'pop'] #.ix is deprecated.

data.loc[data.density > 100, ['pop', 'density']] #掩码与花哨的索引相结合
data.iloc[0, 2]

# 如果对单个标签取值就选择列,而对多个标签用切片就选择行
data['Florida':'Illinois']
# 切片也可以不用索引值,而直接用行数来实现
data[1:3] #即 第二[1]、第三[2] 行
# 掩码操作也可以直接对每一行进行过滤,而不需要使用 loc 索引器
data[data.density > 100]

# 数值运算

rng=np.random.RandomState(42)
ser=pd.Series(rng.randint(0, 10, 4))
df=pd.DataFrame(rng.randint(0, 10, (3, 4)),
                columns=['A', 'B', 'C', 'D'])
np.exp(ser)
np.sin(df * np.pi / 4)

A = pd.Series([2, 4, 6], index=[0, 1, 2])
B = pd.Series([1, 3, 5], index=[1, 2, 3])
A + B
A.add(B, fill_value=0)
A = pd.DataFrame(rng.randint(0, 20, (2, 2)),
                 columns=list('AB'))
B = pd.DataFrame(rng.randint(0, 10, (3, 3)),
                 columns=list('BAC'))
fill=A.stack().mean()
A.add(B, fill_value=fill)

A=rng.randint(10, size=(3, 4))
A - A[0]
df=pd.DataFrame(A, columns=list('QRST'))
df - df.iloc[0]
df.subtract(df['R'], axis=0)
halfrow=df.iloc[0, ::2]
df - halfrow

vals1=np.array([1, None, 3, 4]) #只能用于'object'数组类型
vals2=np.array([1, np.nan, 3, 4]) #特殊浮点数
# 无论和NaN进行何种操作，最终结果都是NaN。
np.nansum(vals2) #可忽略缺失值影响

data=pd.Series([1, np.nan, 'hello', None])
data.isnull()
data[data.notnull()]

data.dropna()
df=pd.DataFrame([[1, np.nan, 2],
                 [2, 3, 5],
                 [np.nan, 4, 6]])
df.dropna() #剔除任何包含NA的整行数据
df.dropna(axis='columns')
df.dropna(axis=1)
df[3]=np.nan
df.dropna(axis='columns', how='all')
df.dropna(axis='rows', thresh=3) # 阈值为最小 *非NaN* 的个数

data=pd.Series([1, np.nan, 2, None, 3], index=list('abcde'))
data.fillna(0)
data.fillna(method='ffill')
df.fillna(method='bfill', axis=1)

# MultiIndex
index = [('California', 2000), ('California', 2010),
         ('New York', 2000), ('New York', 2010),
         ('Texas', 2000), ('Texas', 2010)]
populations = [33871648, 37253956,
               18976457, 19378102,
               20851820, 25145561]
pop = pd.Series(populations, index=index)

pop[[i for i in pop.index if i[1] == 2010]]
# MultiIndex中levels属性表示索引的等级，设置不同的标签。

df=pd.DataFrame(np.random.rand(4, 2),
                index=[['a', 'a', 'b', 'b'],
                [1, 2, 1, 2]],
                columns=['data1', 'data2'])
# 参数设置为至少二维的索引数组，MultiIndex将在后台创建完成。

# 把 将元组作为键 的字典 *传递* 给pandas，将默认转换为MultiIndex
data = {('California', 2000): 33871648,
        ('California', 2010): 37253956,
        ('Texas', 2000): 20851820,
        ('Texas', 2010): 25145561,
        ('New York', 2000): 18976457,
        ('New York', 2010): 19378102}
pd.Series(data)

# 显式创建多级索引
#
pd.MultiIndex.from_arrays([['a', 'a', 'b', 'b'],
                           [1, 2, 1, 2]])
# MultiIndex(levels=[['a', 'b'], [1, 2]],
#            labels=[[0, 0, 1, 1], [0, 1, 0, 1]])
#
pd.MultiIndex.from_tuples([('a', 1),
                           ('a', 2),
                           ('b', 1),
                           ('b', 2)])
# MultiIndex(levels=[['a', 'b'], [1, 2]],
#            labels=[[0, 0, 1, 1], [0, 1, 0, 1]])
#
pd.MultiIndex.from_product([['a', 'b'], [1, 2]])
# MultiIndex(levels=[['a', 'b'], [1, 2]],
#            labels=[[0, 0, 1, 1], [0, 1, 0, 1]])
#
pd.MultiIndex(levels=[['a', 'b'], [1, 2]],
              labels=[[0, 0, 1, 1], [0, 1, 0, 1]])
# MultiIndex(levels=[['a', 'b'], [1, 2]],
#            labels=[[0, 0, 1, 1], [0, 1, 0, 1]])
#
# 创建 Series 或 DataFrame 时,可以将这些对象作为 index 参
# 数,或者通过 reindex 方法更新 Series 或 DataFrame 的索引

# 可以在前面任何一个 MultiIndex 构造器中通过 names 参数设置等级名称,
# 也可以在创建之后通过索引的 names 属性来修改名称
# 从tuple创建：
index = (('California', 2000), ('California', 2010),
         ('New York', 2000), ('New York', 2010),
         ('Texas', 2000), ('Texas', 2010))
# 从product创建：
index = pd.MultiIndex.from_product([['California', 'New York', 'Texas'],
                                    ['2000', '2010']])
populations = [33871648, 37253956,
               18976457, 19378102,
               20851820, 25145561]
pop = pd.Series(populations, index=index)
pop.index.names=['state', 'year']
# 在处理复杂的数据时,为等级设置名称是管理多个索引值的好办法。

# 多级列索引
index = pd.MultiIndex.from_product([[2013, 2014], [1, 2]],
                                   names=['year', 'visit'])
columns = pd.MultiIndex.from_product([['Bob', 'Guido', 'Sue'],
                                      ['HR', 'Temp']],
                                     names=['subject', 'type'])
data=np.round(np.random.randn(4, 6), 1)
data[:, ::2] *= 10
data += 37
health_data = pd.DataFrame(data, index=index, columns=columns)
health_data
health_data['Guido']
